package gateway

import "C"
import (
	"encoding/json"
	"github.com/Unixeno/TheMoon/common/util"
	"github.com/Unixeno/TheMoon/planet/core/context"
	"github.com/Unixeno/TheMoon/planet/orm/model"
	"github.com/Unixeno/TheMoon/planet/orm/moon"
	"github.com/Unixeno/TheMoon/planet/orm/statistics"
	"github.com/Unixeno/TheMoon/planet/zoner"
	"github.com/gorilla/mux"
	"github.com/gorilla/websocket"
	"github.com/jinzhu/gorm"
	"net/http"
	"strconv"
)

// Moon详细信息
// swagger:model MoonDetails
type MoonDetails struct {
	model.Moon
	model.MoonInfo
}

// Moon 信息列表
//
// swagger:response MoonList
type MoonList struct {
	// in:body
	Body []model.Moon
}

// Moon ID response
//
// swagger:model MoonIDResponse
type MoonIDResponse struct {
	// Moon ID
	ID string `json:"id"`
}

// swagger:parameters DeleteMoon InspectMoon
type MoonIDParam struct {
	// MoonID
	//
	// Required: true
	// in: path
	ID string `json:"id"`
}

// swagger:parameters AddMoon
type AddMoonParam struct {
	// 需要添加的MoonID
	//
	// Required: true
	// in: query
	ID string `json:"id"`
	// Moon节点名称，为空时使用MoonID作为节点名
	//
	// in: query
	Name string `json:"name"`
}

// swagger:parameters StatsMoon
type StatsMoonParam struct {
	// in: query
	statistics.MoonStatsParam
}

// Moon Statistics Data
// swagger:response MoonStatsResponse
type StatsResponse struct {
	// in: body
	Body statistics.Stats
}

// swagger:route GET /moon Moon ListMoon
//
// ListMoon
//
// 返回所有Moon节点的基础信息
//
//     Responses:
//       200: MoonList
//       500: body:ErrorResponse
func ListMoon(w http.ResponseWriter, r *http.Request) {
	dao := moon.New()
	moons, err := dao.List()
	if err != nil {
		WriteResponseCode(w, 500, "Server Error")
	}
	WriteResponse(w, moons)
}

// swagger:route POST /moon/add Moon AddMoon
//
// AddMoon
//
// 添加一个Moon节点
//
//     Responses:
//       200: MoonIDResponse
//       409: ConflictError
//       500: ErrorResponse
func AddMoon(w http.ResponseWriter, r *http.Request) {
	param := AddMoonParam{}
	err := json.NewDecoder(r.Body).Decode(&param)
	if err != nil {
		WriteResponseCode(w, 500, ErrorResponse{Message: "Invalid requests parameter"})
		return
	}
	if _, ok := util.ParseMoonID(param.ID); !ok {
		WriteResponseCode(w, 500, ErrorResponse{Message: "Invalid Moon ID"})
		return
	}

	dao := moon.New()

	// 检查是否存在
	if _, err := dao.Get(param.ID); !gorm.IsRecordNotFoundError(err) {
		WriteResponseCode(w, 409, ErrorResponse{Message: "Moon ID already exists"})
		return
	}

	if param.Name == "" {
		param.Name = param.ID
	}

	id, err := dao.Create(model.Moon{
		ID:   param.ID,
		Name: param.Name,
	})
	if err != nil {
		logger.WithError(err).Error()
		WriteResponseCode(w, 500, ErrorResponse{Message: "Server Error"})
		return
	} else {
		WriteResponse(w, MoonIDResponse{ID: id})
		return
	}
}

// swagger:route DELETE /moon/{id} Moon DeleteMoon
//
// DeleteMoon
//
// 删除一个边缘节点
//
//     Responses:
//       204:
//		 404: NotFoundError
//       500: ErrorResponse
func DeleteMoon(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	logger.WithField("id", id).Debug("request to remove moon")
	if _, ok := util.ParseMoonID(id); !ok {
		WriteResponseCode(w, 500, ErrorResponse{Message: "Invalid Moon ID"})
		return
	}
	dao := moon.New()
	err := dao.Delete(id)
	if err == nil {
		WriteResponseCode(w, 204, nil)
		return
	} else if gorm.IsRecordNotFoundError(err) {
		WriteResponseCode(w, 404, ErrorResponse{Message: "Moon Not exist"})
		return
	} else {
		WriteResponseCode(w, 500, ErrorResponse{Message: "Server Error"})
		return
	}
}

type ContainerInfo struct {
	ZoneID string `json:"zone_id"`
	// container id
	ID string `json:"id"`
	// image name
	Image string `json:"image"`
	// container state
	State string `json:"state"`
	// container name
	Name string `json:"name"`
}

// Moon 容器信息列表
//
// swagger:response MoonContainerList
type MoonContainerList struct {
	// in:body
	Body []ContainerInfo `json:"containers"`
}

// swagger:route ListMoonContainers /moon/{id}/containers Moon ListMoonContainers
//
// ListMoonContainers
//
// 列出一个 moon 上的所有容器
//
//     Responses:
//       200: ListMoonContainers
//		 404: NotFoundError
//       500: ErrorResponse
func ListMoonContainers(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	logger.WithField("id", id).Debug("request to get moon containers")
	if _, ok := util.ParseMoonID(id); !ok {
		WriteResponseCode(w, 500, ErrorResponse{Message: "Invalid Moon ID"})
		return
	}

	moonContainersInfo, err := zoner.GetContainers(id)
	if err != nil {
		WriteResponseCode(w, 500, ErrorResponse{Message: err.Error()})
		return
	}

	if moonContainersInfo.ErrorMsg != "" {
		WriteResponseCode(w, 500, ErrorResponse{Message: moonContainersInfo.ErrorMsg})
	}

	resp := MoonContainerList{}
	if moonContainersInfo.Containers != nil {
		resp.Body = make([]ContainerInfo, len(moonContainersInfo.Containers))
		for i, c := range moonContainersInfo.Containers {
			resp.Body[i] = ContainerInfo{
				ZoneID: c.ZoneID,
				ID:     c.ID,
				Image:  c.Image,
				State:  c.State,
				Name:   c.Name,
			}
		}
	}
	WriteResponseCode(w, 200, resp)
}

// swagger:route GET /moon/{id} Moon InspectMoon
//
// InspectMoon
//
// 获取Moon节点的详细信息
//
//     Responses:
//       200: MoonDetails
//		 404: NotFoundError
//       500: ErrorResponse
func InspectMoon(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	if _, ok := util.ParseMoonID(id); !ok {
		WriteResponseCode(w, 500, ErrorResponse{Message: "Invalid Moon ID"})
		return
	}
	dao := moon.New()
	moonDetails, err := dao.Get(id)
	moonExtraInfo, _ := dao.GetMoonInfo()
	if err == nil {
		WriteResponseCode(w, 200, MoonDetails{
			Moon:     moonDetails,
			MoonInfo: moonExtraInfo,
		})
		return
	} else if gorm.IsRecordNotFoundError(err) {
		WriteResponseCode(w, 404, ErrorResponse{Message: "Moon dose not exist"})
		return
	} else {
		WriteResponseCode(w, 500, ServerError)
		return
	}
}

// swagger:route GET /moon/{id}/stats Moon StatsMoon
//
// StatsMoon
//
// 获取Moon节点的统计信息
//
//     Responses:
//       200: MoonStatsResponse
//		 404: NotFoundError
//       500: ErrorResponse
func StatsMoon(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	if _, ok := util.ParseMoonID(id); !ok {
		WriteResponseCode(w, 500, ErrorResponse{Message: "Invalid Moon ID"})
		return
	}
	param := statistics.MoonStatsParam{}
	urlParam := r.URL.Query()
	param.Desc, _ = strconv.ParseBool(urlParam.Get("desc"))
	param.Limit, _ = strconv.Atoi(urlParam.Get("limit"))
	notAfter, _ := strconv.ParseUint(urlParam.Get("not_after"), 10, 32)
	notBefore, _ := strconv.ParseUint(urlParam.Get("not_before"), 10, 32)
	param.NotAfter = uint32(notAfter)
	param.NotBefore = uint32(notBefore)
	param.DataSet = urlParam.Get("data_set")
	param.Disks = urlParam.Get("disks")
	param.NetDev = urlParam.Get("net_dev")

	moonDao := moon.New()
	if _, err := moonDao.Get(id); gorm.IsRecordNotFoundError(err) {
		WriteResponseCode(w, 404, ErrorResponse{Message: "Moon dose not exist"})
		return
	}

	dao := statistics.New()
	if dao == nil {
		logger.Error("failed to create statistics dao")
		WriteResponseCode(w, 500, ServerError)
		return
	}
	dao.MoonID = id
	stats, err := dao.GetStats(param)
	if err != nil {
		logger.WithError(err).Error("failed to get statistics")
		WriteResponseCode(w, 500, ServerError)
	} else {
		WriteResponse(w, stats)
	}
}

func SSH(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	logger.WithField("moonID", id).Debug("request to connect to moon")
	// 检查moon是否存在
	if _, ok := util.ParseMoonID(id); !ok {
		logger.WithField("id", id).Error("invalid moon id")
		WriteResponseCode(w, 500, ErrorResponse{Message: "Invalid Moon ID"})
		return
	}
	upgrader := websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}
	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		logger.WithError(err).Error("websocket upgrade failed")
		return
	}
	defer c.Close()
	stream := context.NewStream("gateway", id, "ssh", 0)
	logger.Debug("websocket connected")

	go func() {
		for {
			_, msg, err := c.ReadMessage()
			if err != nil {
				logger.WithError(err).Error("read websocket failed")
				return
			}
			if err := stream.Send(msg); err != nil {
				return
			}
		}
	}()
	for {
		buf, err := stream.Recv()
		if err != nil {
			logger.WithError(err).Error("stream closed")
			return
		}
		c.WriteMessage(websocket.TextMessage, buf)
	}
}
